/**
 * Copyright 2018 orivil.com. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package actions

import (
	"gitee.com/tugoer/orivil/mux"
	"net/http"
	"mime/multipart"
	"path/filepath"
	"os"
	"io"
	"strings"
	"fmt"
	"bytes"
	"image"
	"github.com/nfnt/resize"
	"image/png"
	"image/draw"
	"image/jpeg"
	"time"
	"strconv"
	"gitee.com/tugoer/orivil/param"
	"gitee.com/tugoer/psy-testing/code/api/tags"
)

const (
	ImageDir = "images"
)

var staticServer = http.FileServer(http.Dir("./"))

// 接收上传图片并返回图片地址
func HandleImage(dir, watermark string, width, height, quality, wx, wy int, header *multipart.FileHeader) (images []string, err error) {
	var name string
	if dir != "" {
		dir = filepath.Join(ImageDir, dir)
		name = header.Filename
	} else {
		dir, name = getNowDir(ImageDir)
		name += filepath.Ext(header.Filename)
	}
	err = os.MkdirAll(dir, 0777)
	if err != nil {
		return
	}

	filename := filepath.Join(dir, name)
	src, e := header.Open()
	if e != nil {
		return nil, e
	}
	defer src.Close()

	buf := bytes.NewBuffer(nil)

	if width != 0 || height != 0 || watermark != "" {
		img, _, e := image.Decode(src)
		if e != nil {
			return nil, e
		}
		// 缩放
		if width != 0 || height != 0 {
			img = resize.Resize(uint(width), uint(height), img, resize.Lanczos3)
		}
		// 水印
		if watermark != "" {
			wmb, e := os.Open(watermark)
			if e == nil {
				defer wmb.Close()
				wm, e := png.Decode(wmb)
				if e != nil {
					return nil, e
				}
				offset := image.Pt(wx, wy)
				b := img.Bounds()
				m := image.NewRGBA(b)
				draw.Draw(m, b, img, image.ZP, draw.Src)
				draw.Draw(m, wm.Bounds().Add(offset), wm, image.ZP, draw.Over)
				img = m
			} else if !os.IsNotExist(e)  {
				return nil, e
			}
		}

		if quality == 0 {
			quality = 50
		}
		err = jpeg.Encode(buf, img, &jpeg.Options{
			Quality: quality,
		})
		if err != nil {
			return nil, err
		}
	} else {
		_, err = io.Copy(buf, src)
		if err != nil {
			return nil, err
		}
	}

	f, e := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777)
	if e != nil {
		return nil, e
	}
	defer f.Close()
	_, err = io.Copy(f, buf)
	if err != nil {
		return
	}
	images = append(images, filepath.ToSlash(filename))
	return images, nil
}


func getNowDir(baseDir string) (dir string, filename string) {
	now := time.Now()
	year, month, day := now.Date()
	dir = filepath.Join(baseDir, strconv.Itoa(year), strconv.Itoa(int(month)), strconv.Itoa(day))
	filename = strconv.FormatInt(now.UnixNano(), 10)
	return
}

var Images = []*mux.Action{
	{
		Name: "Get Picture",
		Method: http.MethodGet,
		Route: "images/",
		Handler: func(ctx *mux.Context) {
			staticServer.ServeHTTP(ctx.Response, ctx.Request)
		},
		ApiDoc: &mux.ApiDoc{
			Tags: []string{tags.Images},
			Summary: "图片服务",
			Description: fmt.Sprintf("静态文件服务器, [点击](%s/images) 浏览", mux.Env.Listen.Addr()),
			Responses: mux.Responses{
				"200":{
					Description:"success",
				},
				"404":{
					Description:"not found",
				},
			},
		},
	},
	{
		Name: "Upload Picture",
		Method: http.MethodPost,
		Route: "images",
		Params: mux.Parameters{
			{
				In: mux.Form,
				Schema: &uploadPicture{},
				Validator:UploadPictureValidator(),
			},
		},
		Handler: func(ctx *mux.Context) {
			var images []string
			p := &uploadPicture{}
			p.Images = func(field string, header *multipart.FileHeader) (err error) {
				images, err = HandleImage(p.Dir, p.WalterMark, p.Width, p.Height, p.Quality, p.WMX, p.WMY, header)
				return
			}
			ctx.WarnParse(p)
			ctx.SendJson(images)
		},
		ApiDoc: &mux.ApiDoc{
			Tags: []string{tags.Images},
			Summary: "上传图片",
			Description: "图片有大小限制, 如果图片过大, 需要处理之后再上传",
			Responses: mux.Responses{
				"200":{
					Description:"返回图片地址",
					Example: []string{"images/2018/5/6/123456789.jpg", "images/2018/5/6/123456789.png"},
				},
			},
		},
	},
	{
		Name: "Delete Picture",
		Method: http.MethodDelete,
		Route: "images",
		Params: mux.Parameters{
			{
				In: mux.Query,
				Schema: &deletePicture{},
			},
		},
		Handler: func(ctx *mux.Context) {
			p := &deletePicture{}
			ctx.WarnParse(p)
			RemoveImage(p.Name, ctx)
			ctx.MsgSuccess("删除成功", "")
		},
		ApiDoc: &mux.ApiDoc{
			Tags: []string{tags.Images},
			Summary: "删除图片",
			Description: fmt.Sprintf("只能删除'%s'文件夹下的文件", ImageDir),
		},
	},
}

func RemoveImage(file string, ctx *mux.Context) {
	if strings.HasPrefix(file, ImageDir) {
		err := os.Remove(file)
		if err != nil {
			if strings.Contains(err.Error(), "The directory is not empty.") {
				ctx.MsgWarning("只能删除空文件夹", "")
			} else {
				ctx.ErrWarning(err)
			}
		}
	} else {
		ctx.MsgDanger(fmt.Sprintf("只能删除'%s'文件夹下的文件", ImageDir), "")
	}
}

type uploadPicture struct {
	Width int `desc:"图片固定宽度"`
	Height int `desc:"图片固定高度"`
	Quality int `desc:"图片质量(1-100), 默认50"`
	WalterMark string `desc:"水印图片地址(images/...), 如果图片不存在, 则将跳过水印添加"`
	WMX int `desc:"水印图片x坐标"`
	WMY int `desc:"水印图片y坐标"`
	Dir string `desc:"图片上传目录"`
	Images param.FileHandler `desc:"大小必须在2-900KB" mime:"image" mime-msg:"图片格式不正确" size:"2-900" size-msg:"图片大小必须在2-900KB"`
}

func UploadPictureValidator() *param.Validator {
	p := &uploadPicture{}
	v := param.NewEmptyValidator(p)
	v.Field(&p.Images).Required("未上传任何图片")
	return v
}

type deletePicture struct {
	Name string `required:"未获得删除的文件名" desc:"需要删除图片的名称"`
}
